/**
 * Пример использования. Scrollpane ресайзится при изменении размеров сцены.
 * 
 * @example
<listing>
package {
	import peace.gui.controls.scroll.Scrollpane;
	
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;

[SWF(backgroundColor="0xffffff")]
public class Main extends Sprite
{
	public function Main()
	{
		stage.scaleMode = StageScaleMode.NO_SCALE;
		stage.align = StageAlign.TOP_LEFT;
		stage.frameRate = 31;
		
		var c:Shape = new Shape();
		c.graphics.beginFill(0xff0000);
		c.graphics.drawCircle(200, 200, 200);
		
		var sp:Scrollpane = new Scrollpane(stage.stageWidth, stage.stageHeight);
		addChild(sp);
		sp.content = c;

 		stage.addEventListener(Event.RESIZE, function (e:Event):void
 		{
 			sp.width = stage.stageWidth;
 			sp.height = stage.stageHeight;
 		});
	}
}

}
</listing>
*/

package peace.gui.controls.scroll
{
import flash.display.DisplayObject;
import flash.display.InteractiveObject;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;

import peace.gui.controls.RubberSprite;
import peace.gui.dnd.DragEvent;
import peace.gui.dnd.DragListener;
/**
* 
*/
[Event(name="scrollPoint", type="peace.gui.controls.scroll.ScrollEvent")]
/**
* 
*/
[Event(name="showHorizontalBar", type="peace.gui.controls.scroll.Scrollpane")]
/**
* 
*/
[Event(name="hideHorizontalBar", type="peace.gui.controls.scroll.Scrollpane")]
/**
* 
*/
[Event(name="showVerticalBar", type="peace.gui.controls.scroll.Scrollpane")]
/**
* 
*/
[Event(name="hideVerticalBar", type="peace.gui.controls.scroll.Scrollpane")]
/**

 * 
 */
public class Scrollpane extends RubberSprite
{
	// События видимости баров
	public static const SHOW_HORIZONTAL_BAR:String = "showHorizontalBar";
	public static const HIDE_HORIZONTAL_BAR:String = "hideHorizontalBar";
	public static const SHOW_VERTICAL_BAR:String = "showVerticalBar";
	public static const HIDE_VERTICAL_BAR:String = "hideVerticalBar";
	/**
	 * 
	 */
	public var hbar:Scrollbar;
	/**
	 * 
	 */
	public var vbar:Scrollbar;
	/**
	 * замораживаем событие изменения изменения размера контента 
	 */
	public var freezeSize:Boolean = false;
	/**
	 * замораживаем событие скроллинга (полезно для скроллинга в точку) 
	 */
	public var freezeScroll:Boolean = false;
	/**
	 * 
	 */
	protected var mouseShield:Sprite = new Sprite(); // глубина 0
	/**
	 * 
	 */
	protected var dndShield:DragListener = new DragListener(); // глубина 1 
	/**
	 * 
	 */
	protected var _content:DisplayObject=null; // глубина 1 или 2 (dragEnabled==true)!
	/**
	 * 
	 */
	protected var contentMask:Shape = new Shape();
	/**
	 * 
	 */	
	protected var hasHorizontalBar:Boolean;
	/**
	 * 
	 */
	protected var hasVerticalBar:Boolean;
	/**
	 * для ДНД
	 */
	protected var startDragPoint:Point;
	/**
	 * 
	 * @param width
	 * @param height
	 * @param hasHorizontalBar если нет, не появится даже когда нужен
	 * @param hasVerticalBar если нет, не появится даже когда нужен
	 * 
	 */	
	public function Scrollpane (width:Number=200, height:Number=200,
								hasHorizontalBar:Boolean=true, hasVerticalBar:Boolean=true)
	{
		mouseShield.graphics.lineStyle(0,0,0);
		mouseShield.graphics.beginFill(0xff0000,0);
		mouseShield.graphics.drawRect(0,0,10,10);
		addChildAt(mouseShield,0);

		dndShield.graphics.beginFill(0,0);
		dndShield.graphics.drawRect(0,0,10,10);
		// по умолчанию не таскаем
		dragEnabled = false;

		contentMask.graphics.beginFill(0, 0);
		contentMask.graphics.drawRect(0,0,10,10);
		addChild(contentMask);
		
		hbar = createScrollbar (width, true);
		hbar.visible = false;
		hbar.addEventListener(ScrollEvent.SCROLL_X, onScroll, false, 0, true);
		hbar.addEventListener(ScrollEvent.CONTENT_SIZE_CHANGED, onContentSizeChanged, false, 0, true);
		addChild(hbar);

		vbar = createScrollbar (height, false);
		vbar.visible = false;
		vbar.addEventListener(ScrollEvent.SCROLL_Y, onScroll, false, 0, true);
		vbar.addEventListener(ScrollEvent.CONTENT_SIZE_CHANGED, onContentSizeChanged, false, 0, true);
		addChild(vbar);
		
		resize(width, height);
		
		this.hasHorizontalBar = hasHorizontalBar;
		this.hasVerticalBar = hasVerticalBar;

		addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel, false, 0, true);
	}
	/**
	 * 
	 * @param value
	 * 
	 */	
	public function set content(value:DisplayObject):void
	{
		if (_content)
		{
			removeChild(_content);
		}
		
		_content = value;
		
		if(_content)
		{
			_content.mask = contentMask;
			// вставляем перед маской
			addChildAt(_content, getChildIndex(contentMask));
			
			// даже если их не видно, они есть!
			hbar.content = _content;
			vbar.content = _content;
			
			checkBarsVisible();
		}
	}
	/**
	 * 
	 * @return 
	 * 
	 */	
	public function get content ():DisplayObject
	{
		return _content;
	}
	/**
	 * 
	 * @param n
	 * 
	 */
	public function set scrollPositionX (n:Number):void
	{
		hbar.scrollPosition = n;
	}
	/**
	 * 
	 * @return 
	 * 
	 */
	public function get scrollPositionX ():Number
	{
		return hbar.scrollPosition;
	}
	/**
	 * 
	 * @param n
	 * 
	 */
	public function set scrollPositionY (n:Number):void
	{
		vbar.scrollPosition = n;
	}
	/**
	 * 
	 * @return 
	 * 
	 */
	public function get scrollPositionY ():Number
	{
		return vbar.scrollPosition;
	}
	/**
	 * 
	 * @param p
	 * 
	 */	
	public function scrollToPoint (p:Point):void
	{
		freezeScroll = true;
		hbar.scrollPosition = p.x;
		freezeScroll = false;
		vbar.scrollPosition = p.y;
	}
	/**
	 * 
	 * @return 
	 * 
	 */
	public function get horizontalBarVisible ():Boolean
	{
		return hbar ? hbar.visible : false;
	}
	/**
	 * 
	 * @return 
	 * 
	 */
	public function get verticalBarVisible ():Boolean
	{
		return vbar ? vbar.visible : false;
	}
	/**
	 * 
	 * @return 
	 * 
	 */
	public function get verticalBarWidth ():Number
	{
		return verticalBarVisible ? vbar.width : 0;
	}
	/**
	 * 
	 * @return 
	 * 
	 */
	public function get horizontalBarHeight ():Number
	{
		return horizontalBarVisible ? hbar.height : 0;
	}
	/**
	 * 
	 * @param value
	 * 
	 */	
	public function set scaleRunner (value:Boolean):void
	{
		hbar.scaleRunner = value;
		vbar.scaleRunner = value;
	}
	/**
	 * 
	 * @param up
	 * @param down
	 * @param runner
	 * @param track
	 * 
	 */	
	public function setHorizontalBarSkin ( 	up:InteractiveObject, down:InteractiveObject,
	                                       	runner:InteractiveObject, track:InteractiveObject):void
	{
		hbar.setSkin(up,down,runner,track);
	}
	/**
	 * 
	 * @param up
	 * @param down
	 * @param runner
	 * @param track
	 * 
	 */
	public function setVerticalBarSkin (    up:InteractiveObject, down:InteractiveObject,
	                                        runner:InteractiveObject, track:InteractiveObject):void
	{
		vbar.setSkin(up,down,runner,track);
	}
	/**
	 * Можно ли таскать/скроллить за контент 
	 */	
	public function set dragEnabled (b:Boolean):void
	{
		dndShield.enabled = b;
		if (b)
		{
			addChildAt(dndShield,1);
			dndShield.addEventListener(DragEvent.START_DRAG, onDragStart, false,0,true);
			dndShield.addEventListener(DragEvent.MOVE, onDragMove, false,0,true);
		}
		else
		{
			if (contains(dndShield))
			{
				removeChild(dndShield);
			}
			dndShield.removeEventListener(DragEvent.START_DRAG, onDragStart);
			dndShield.removeEventListener(DragEvent.MOVE, onDragMove);
		}
	}	
	/**
	 * 
	 * @return 
	 * 
	 */
	public function get dragEnabled ():Boolean
	{
		return dndShield.enabled;
	}
	/**
	 * Принудительно перерисовываем скроллбары при изменении размеров контента. <br> 
	 * Без ожидания Event.ENTER_FRAME 
	 */	
	public function redraw (raiseResizeEvents:Boolean=false, raiseScrollEvents:Boolean=false):void
	{
		var cachedFreezeSize:Boolean = freezeSize;
		var cachedFreezeScroll:Boolean = freezeScroll;
		
		freezeScroll = !raiseScrollEvents;
		freezeSize = !raiseResizeEvents;
		
		hbar.redraw();
		vbar.redraw();
		
		freezeScroll = cachedFreezeScroll;
		freezeSize = cachedFreezeSize;
		
		checkBarsVisible();
	}
	/**
	 * 
	 * 
	 */	
	override public function arrange():void
	{
		var n:Number;
		if (widthChanged || heightChanged)
		{
			checkBarsVisible();

			mouseShield.width = size.width;
			mouseShield.height = size.height;
		}
	}
	/**
	 * конструктор скроллбаров
	 * 
	 * @return 
	 * 
	 */	
	protected function createScrollbar (size:Number, isHorizontal:Boolean):Scrollbar
	{
		return new Scrollbar(size, isHorizontal);
	}
	/**
	 * Проверяем, нужны ли скроллбары в текущей ситуации 
	 * 
	 */	
	private function checkBarsVisible ():void
	{
		if(! _content) return;
		
		var w:Number = size.width;
		var h:Number = size.height;
		var shortW:Number = w - vbar.width;
		var shortH:Number = h - hbar.height;
		
		// вычисляем маркеры видимости баров
		var needHbar:Boolean = hasHorizontalBar && _content.width > w;		
		var needVbar:Boolean = hasVerticalBar && _content.height > h;
		if (needHbar)
		{
			needVbar = hasVerticalBar && _content.height > shortH;
		}
		else if (needVbar)
		{
			needHbar = hasHorizontalBar && _content.width > shortW;		
		}
		
		// выставляем размеры
		var s:Number;
		if (needHbar)
		{
			s = needVbar ? shortW : w;
			if (hbar.size != s) hbar.size = s;
		}
		if (needVbar)
		{
			s = needHbar ? shortH : h;
			if (vbar.size != s) vbar.size = s;
		}
		
		// файрим события
		if (hbar.visible && !needHbar)
		{
			// отскролливаем назад если чо
			if (hbar.scrollPosition > 0) hbar.scrollPosition = 0;
			dispatchEvent(new Event(HIDE_HORIZONTAL_BAR));
		}
		else if (!hbar.visible && needHbar)
		{
			dispatchEvent(new Event(SHOW_HORIZONTAL_BAR));
		}
		
		if (vbar.visible && !needVbar)
		{
			// отскролливаем назад если чо
			if (vbar.scrollPosition > 0) vbar.scrollPosition = 0;
			dispatchEvent(new Event(HIDE_VERTICAL_BAR));
		}
		else if (!vbar.visible && needVbar)
		{
			dispatchEvent(new Event(SHOW_VERTICAL_BAR));
		}
		
		// видимость
		hbar.visible = needHbar;
		vbar.visible = needVbar;

		// чтобы белых полос не оставалось от проёбаного скроллбара
		contentMask.width = needVbar ? shortW : w;
		contentMask.height = needHbar ? shortH : h;
		dndShield.width = contentMask.width;
		dndShield.height = contentMask.height;
		
		vbar.x = shortW;
		hbar.y = shortH;
	}
	/**
	 * 
	 * @param e
	 * 
	 */
	private function onMouseWheel (e:MouseEvent):void
	{
		if (vbar.visible)
		{
			var delta:Number = vbar.scrollStep * 5;
			vbar.scrollPosition += (e.delta>0) ? -delta : delta;
			
			// локализуем колесо мышки внутри контрола 
			e.stopPropagation();
		}
	}
	/**
	 * 
	 * @param e
	 * 
	 */	
	private function onScroll (e:ScrollEvent):void
	{
		if (! freezeScroll)
		{
			dispatchEvent(new ScrollEvent(ScrollEvent.SCROLL_POINT, 
									new Point(hbar.scrollPosition, vbar.scrollPosition)));
		}
	}
	/**
	 * 
	 * @param e
	 * 
	 */	
	private function onContentSizeChanged(e:ScrollEvent):void
	{
		if(!freezeSize)
		{
			checkBarsVisible();
		}
	}
	/**
	 * 
	 * @param e
	 * 
	 */
	private function onDragStart (e:DragEvent):void
	{
		startDragPoint = new Point(hbar.scrollPosition, vbar.scrollPosition);
	}
	/**
	 * 
	 * @param e
	 * 
	 */
	private function onDragMove (e:DragEvent):void
	{
		hbar.scrollPosition = startDragPoint.x - e.point.x;
		vbar.scrollPosition = startDragPoint.y - e.point.y;
	}

}
}